﻿namespace TomatoBreak.UnitSpecs.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Web.Mvc;

    using Machine.Specifications;
    using NSubstitute;

    using DataAccess;
    using TomatoBreak.DomainObjects;
    using ExtensionMethods;
    using Infrastructure;
    using Web;

    public abstract class UserControllerSpec
    {
        protected static IRepository<User> repository;
        protected static IFormsAuthentication formsAuthentication;

        protected static UserController controller;

        Establish context = () =>
        {
            repository = Substitute.For<IRepository<User>>();
            formsAuthentication = Substitute.For<IFormsAuthentication>();

            controller = new UserController(repository, formsAuthentication);
        };
    }

    [Subject(typeof(UserController))]
    public class when_creating_user_with_valid_value : UserControllerSpec
    {
        const string UserName = "KaziManzurRashid";

        static HttpStatusCodeResult result;

        Because of = () => result = (HttpStatusCodeResult)controller.Create(new CreateUserModel
                                                                                {
                                                                                    UserName = UserName, 
                                                                                    Password = "secret", 
                                                                                    Confirm = "secret", 
                                                                                    Email = "kazimanzurrashid@gmail.com"
                                                                                });

        It should_add_user_to_repository = () => repository.Received().Add(Arg.Any<User>());

        It should_sign_in_user = () => formsAuthentication.Received().SignIn(UserName, false);

        It should_return_http_created = () => result.StatusCode.ShouldEqual(201);
    }

    [Subject(typeof(UserController))]
    public class when_creating_user_with_invalid_model_state : UserControllerSpec
    {
        static JsonResult result;

        Establish context = () =>
        {
            controller.ModelState.AddModelError("UserName", "User name cannot be blank");
            controller.ModelState.AddModelError("Password", "Password cannot be blank");
            controller.ModelState.AddModelError("Confirm", "Confirm password cannot be blank");
            controller.ModelState.AddModelError("Email", "Email cannot be blank");
        };

        Because of = () => result = (JsonResult)controller.Create(new CreateUserModel());

        It should_return_model_state_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().Count().ShouldEqual(4);
    }

    [Subject(typeof(UserController))]
    public class when_creating_user_with_duplicate_name : UserControllerSpec
    {
        static JsonResult result;

        Establish context = () => repository.Exists(Arg.Any<Expression<Func<User, bool>>>()).Returns(true);

        Because of = () => result = (JsonResult)controller.Create(new CreateUserModel());

        It should_return_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().ShouldNotBeEmpty();
    }

    [Subject(typeof(UserController))]
    public class when_creating_user_with_duplicate_email : UserControllerSpec
    {
        static JsonResult result;

        Establish context = () =>
        {
            var existsRequestQueue = new Queue<bool>();
            existsRequestQueue.Enqueue(false);
            existsRequestQueue.Enqueue(true);

            repository.Exists(Arg.Any<Expression<Func<User, bool>>>()).Returns(info => existsRequestQueue.Dequeue());
        };

        Because of = () => result = (JsonResult)controller.Create(new CreateUserModel());

        It should_return_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().ShouldNotBeEmpty();
    }
}